#include "Configuraciones.h"
#include <stdexcept>
#include <fstream>



Configuraciones::Configuraciones(std::string nomArch, bool crear){
	try {
		ticpp::Document xml;

		// Chequea si existe o hay que crear un archivo de configuraciones
		if (crear || !existeArch(nomArch)){

			// Crea un archivo con valores por defecto y lo guarda
			crearConfigXML(xml);
			xml.SaveFile(nomArch);
		}

		// Carga el archivo a parsear
		xml.LoadFile(nomArch);

		// Obtiene el "root node"
		ticpp::Element* root = xml.FirstChildElement();

		// Carga los usuarios en el mapa
		ticpp::Element* nodo = root->FirstChildElement("usuarios");
		procesarListado(mapUsuarios, nodo, "usuario", "clave");

		// Carga los tipos MIME estaticos en el mapa
		nodo = root->FirstChildElement("tipoEstatico");
		procesarListado(mapTiposEstaticos, nodo, "extension", "contentType");

		// Carga los tipos MIME dinamicos en el mapa
		nodo = root->FirstChildElement("tipoDinamico");
		procesarListado(mapTiposDinamicos, nodo, "extension", "comando");

		// Carga los codigos de errores en el mapa
		nodo = root->FirstChildElement("pagErrores");
		procesarListado(mapPaginasErrores, nodo, "codigo", "pagina");

		// Carga las configuraciones basicas
		nodo = root->FirstChildElement("basica");
		procesarConfiguracionBasica(nodo);

		// Carga las configuraciones extras
		nodo = root->FirstChildElement("extra");
		procesarConfiguracionExtra(nodo);

		// Guarda el nombre del archivo de configuraciones
		this->_nomArchXML = nomArch;

	} catch (ticpp::Exception& ex){
		throw std::runtime_error("Error al parsear el XML de configuraciones");
	}
}


Configuraciones::~Configuraciones(){
}


std::string Configuraciones::getNombreArch(){
	return this->_nomArchXML;
}


void Configuraciones::procesarListado(std::map<std::string, std::string> &mapa, ticpp::Element* nodo,
	std::string key, std::string value)
{
	// Itera atraves de los hijo del nodo
	ticpp::Iterator<ticpp::Element> it;
	for (it = it.begin(nodo); it != it.end(); it++){
		// Extrae los valores
		std::string clave;
		std::string valor;
		it->FirstChildElement(key)->GetText(&clave);
		it->FirstChildElement(value)->GetText(&valor);

		// Los agrega al mapa
		mapa[clave] = valor;
	}
}


void Configuraciones::procesarConfiguracionBasica(ticpp::Element* nodo){
	// Procesa uno de los tags a la vez
	nodo->FirstChildElement("direcBase")->GetText(&this->baseDir);
	nodo->FirstChildElement("nroPuerto")->GetText(&this->nroPuerto);
	nodo->FirstChildElement("authBasica")->GetText(&this->autenticacionBasica);
	nodo->FirstChildElement("nroPuertoControl")->GetText(&this->nroPuertoCtrl);
	nodo->FirstChildElement("connMaxCliente")->GetText(&this->maxConexionesCliente);
	nodo->FirstChildElement("connMaxSimultaneas")->GetText(&this->maxConexionesSimultaneas);
	nodo->FirstChildElement("timeout")->GetText(&this->timeout);
}


void Configuraciones::procesarConfiguracionExtra(ticpp::Element* nodo){
	nodo->FirstChildElement("serverId")->GetText(&this->serverId);
	nodo->FirstChildElement("nombreServer")->GetText(&this->nomServer);
	nodo->FirstChildElement("logAccesos")->GetText(&this->pathLogAccesos);
	nodo->FirstChildElement("logErrores")->GetText(&this->pathLogErrores);
	nodo->FirstChildElement("modoGuiZilla")->GetText(&this->modoZilla);
}


void Configuraciones::guardar(){
	// Carga todos los datos en un nuevo XML, lo arma por secciones
	// Configuracion basica
	ticpp::Element basica("basica");
	ticpp::Element dirBase("direcBase", this->baseDir);
	ticpp::Element puerto("nroPuerto", this->nroPuerto);
	ticpp::Element puertoCtrl("nroPuertoControl", this->nroPuertoCtrl);
	ticpp::Element autentificar("authBasica", this->autenticacionBasica);
	ticpp::Element maxConnCliente("connMaxCliente", this->maxConexionesCliente);
	ticpp::Element maxConn("connMaxSimultaneas", this->maxConexionesSimultaneas);
	ticpp::Element timeout("timeout", this->timeout);
	basica.InsertEndChild(puerto);
	basica.InsertEndChild(maxConn);
	basica.InsertEndChild(dirBase);
	basica.InsertEndChild(puertoCtrl);
	basica.InsertEndChild(autentificar);
	basica.InsertEndChild(maxConnCliente);
	basica.InsertEndChild(timeout);

	// Configuracion extra
	ticpp::Element extra("extra");
	ticpp::Element serverId("serverId", this->serverId);
	ticpp::Element nomServer("nombreServer", this->nomServer);
	ticpp::Element pathAcc("logAccesos", this->pathLogAccesos);
	ticpp::Element pathErr("logErrores", this->pathLogErrores);
	ticpp::Element GuiZilla("modoGuiZilla", this->modoZilla);
	extra.InsertEndChild(serverId);
	extra.InsertEndChild(nomServer);
	extra.InsertEndChild(pathAcc);
	extra.InsertEndChild(pathErr);
	extra.InsertEndChild(GuiZilla);

	// Listados, carga los mapas en el nodo
	ticpp::Element errores = buildListado(this->mapPaginasErrores, "pagErrores", "tipo", "codigo", "pagina");
	ticpp::Element usuarios = buildListado(this->mapUsuarios, "usuarios", "credencial", "usuario", "clave");
	ticpp::Element dinamico = buildListado(this->mapTiposDinamicos, "tipoDinamico", "tipo", "extension", "comando");
	ticpp::Element estatico = buildListado(this->mapTiposEstaticos, "tipoEstatico", "tipo", "extension", "contentType");

	// El elemento raiz del XML
	ticpp::Element raiz("configuraciones");
	raiz.InsertEndChild(basica);
	raiz.InsertEndChild(extra);
	raiz.InsertEndChild(estatico);
	raiz.InsertEndChild(dinamico);
	raiz.InsertEndChild(errores);
	raiz.InsertEndChild(usuarios);

	// El documento y su declaracion
	ticpp::Document xml;
	ticpp::Declaration dec("1.0", "UTF-8", "no");

	xml.InsertEndChild(dec);
	xml.InsertEndChild(raiz);

	// Sobre-escribe el xml que ya existe
	xml.SaveFile(this->_nomArchXML);
}


ticpp::Element Configuraciones::buildListado(std::map<std::string, std::string> mapa, std::string grupo,
	std::string fila, std::string key, std::string value)
{
	// El nodo que contiene a todo el resto
	ticpp::Element grupoNodos(grupo);

	// Itera por todos los elementos del mapa
	std::map<std::string, std::string>::iterator mi;
	for (mi = mapa.begin(); mi != mapa.end(); mi++){
		// Arma el par clave-valor
		ticpp::Element clave(key, mi->first);
		ticpp::Element valor(value, mi->second);

		// Los agrega a la fila y esta al nodo principal
		ticpp::Element filaDatos(fila);
		filaDatos.InsertEndChild(clave);
		filaDatos.InsertEndChild(valor);
		grupoNodos.InsertEndChild(filaDatos);
	}

	// Devuelve el grupo con todos los nodos dentro
	return grupoNodos;
}


bool Configuraciones::existeArch(std::string nomArch){
	// Trata de abrir el archivo, si lo logro existe
	std::fstream f(nomArch.c_str());
	bool existeArch = f.good();
	f.close();

	return existeArch;
}


void Configuraciones::crearConfigXML(ticpp::Document& xml){
	// La declaracion
	ticpp::Declaration dec("1.0", "UTF-8", "no");
	xml.InsertEndChild(dec);

	// El elemento raiz del xml
	ticpp::Element raiz("configuraciones");

	// Las divisiones de los parametros
	ticpp::Element basica("basica");
	ticpp::Element extra("extra");
	ticpp::Element estatico("tipoEstatico");
	ticpp::Element dinamico("tipoDinamico");
	ticpp::Element errores("pagErrores");
	ticpp::Element usuarios("usuarios");

	// Configuracion basica
	ticpp::Element puerto("nroPuerto", 4000);
	ticpp::Element puertoCtrl("nroPuertoControl", 4444);
	ticpp::Element maxConn("connMaxSimultaneas", 20);
	ticpp::Element maxConnCliente("connMaxCliente", 2);
	ticpp::Element dirBase("direcBase", "/www/root/");
	ticpp::Element autentificar("authBasica", false);
	ticpp::Element timeout("timeout", 20);
	basica.InsertEndChild(puerto);
	basica.InsertEndChild(puertoCtrl);
	basica.InsertEndChild(maxConn);
	basica.InsertEndChild(maxConnCliente);
	basica.InsertEndChild(dirBase);
	basica.InsertEndChild(autentificar);
	basica.InsertEndChild(timeout);

	// Configuraciones extras
	ticpp::Element serverId("serverId", "TPFinal");
	ticpp::Element nomServer("nombreServer", "TPFinal");
	ticpp::Element pathAccesos("logAccesos", "accesos.log");
	ticpp::Element pathErrores("logErrores", "errores.log");
	ticpp::Element guiZilla("modoGuiZilla", false);
	extra.InsertEndChild(serverId);
	extra.InsertEndChild(nomServer);
	extra.InsertEndChild(pathAccesos);
	extra.InsertEndChild(pathErrores);
	extra.InsertEndChild(guiZilla);

	// Tipos MIME estaticos
	ticpp::Element tipoE1("tipo");
	ticpp::Element extensionE1("extension", "html");
	ticpp::Element contentType1("contentType", "text/html");
	tipoE1.InsertEndChild(extensionE1);
	tipoE1.InsertEndChild(contentType1);

	ticpp::Element tipoE2("tipo");
	ticpp::Element extensionE2("extension", "gif");
	ticpp::Element contentType2("contentType", "image/gif");
	tipoE2.InsertEndChild(extensionE2);
	tipoE2.InsertEndChild(contentType2);

	estatico.InsertEndChild(tipoE1);
	estatico.InsertEndChild(tipoE2);

	// Tipos MIME dinamicos
	ticpp::Element tipoD1("tipo");
	ticpp::Element extensionD1("extension", "php");
	ticpp::Element comando1("comando", "/usr/bin/php-cgi {NAME}");
	tipoD1.InsertEndChild(extensionD1);
	tipoD1.InsertEndChild(comando1);

	ticpp::Element tipoD2("tipo");
	ticpp::Element extensionD2("extension", "cgi");
	ticpp::Element comando2("comando", "{NAME}");
	tipoD2.InsertEndChild(extensionD2);
	tipoD2.InsertEndChild(comando2);

	dinamico.InsertEndChild(tipoD1);
	dinamico.InsertEndChild(tipoD2);

	// Paginas de errores
	ticpp::Element error("tipo");
	ticpp::Element codigo("codigo", 404);
	ticpp::Element pagina("pagina", "404.html");
	error.InsertEndChild(codigo);
	error.InsertEndChild(pagina);

	errores.InsertEndChild(error);

	// Paginas de usuarios
	ticpp::Element user("credencial");
	ticpp::Element usuario("usuario", "Pepe");
	ticpp::Element clave("clave", 1234);
	user.InsertEndChild(usuario);
	user.InsertEndChild(clave);

	usuarios.InsertEndChild(user);

	// Se insertan todas las secciones a la raiz del XML
	raiz.InsertEndChild(basica);
	raiz.InsertEndChild(extra);
	raiz.InsertEndChild(estatico);
	raiz.InsertEndChild(dinamico);
	raiz.InsertEndChild(errores);
	raiz.InsertEndChild(usuarios);
	xml.InsertEndChild(raiz);
}


